#include "SQLUpdateStatement.h"

#include "../../../DbType.h"
#include "../../SQLUtils.h"
#include "../expr/SQLBinaryOpExpr.h"
#include "../expr/SQLBinaryOpExprGroup.h"
#include "../expr/SQLBinaryOperator.h"
#include "../../visitor/SQLASTVisitor.h"
#include "../../../utils/instanceof.h"
#include "../../../utils/listAddAll.h"
#include "../../../utils/ifeq.h"
#include "../SQLName.h"
#include "../SQLExpr.h"

#include "SQLAssignItem.h"
#include "SQLUpdateSetItem.h"
#include "SQLTableSource.h"
#include "SQLExprTableSource.h"
#include "SQLWithSubqueryClause.h"
#include "SQLJoinTableSource.h"

SQLUpdateStatement::SQLUpdateStatement()
{
  items = std::make_shared<std::list<SQLUpdateSetItem_ptr>>();
  returning = std::make_shared<std::list<SQLExpr_ptr>>();
  partitions = std::make_shared<std::list<SQLAssignItem_ptr>>();
}

void SQLUpdateStatement::cloneTo(SQLUpdateStatement_ptr x)
{
  x->dbType = dbType;
  x->afterSemi = afterSemi;

  if (with != NULL)
  {
    x->setWith(std::dynamic_pointer_cast<SQLWithSubqueryClause>(with->clone()));
    x->with->setParent(x);
  }

  if (where != NULL)
  {
    x->where = std::dynamic_pointer_cast<SQLExpr>(where->clone());
    x->where->setParent(x);
  }
  if (tableSource != NULL)
  {
    x->setTableSource(std::dynamic_pointer_cast<SQLTableSource>(tableSource->clone()));
    x->tableSource->setParent(x);
  }

  for (SQLUpdateSetItem_ptr item : *items)
  {
    SQLUpdateSetItem_ptr clone = std::dynamic_pointer_cast<SQLUpdateSetItem>(item->clone());
    clone->setParent(x);
    x->getItems()->push_back(clone);
  }

  if (!returning->empty())
  {
    for (SQLExpr_ptr item : *returning)
    {
      SQLExpr_ptr clone = std::dynamic_pointer_cast<SQLExpr>(item->clone());
      clone->setParent(x);
      x->getReturning()->push_back(clone);
    }
  }

  if (orderBy != NULL)
  {
    x->orderBy = std::dynamic_pointer_cast<SQLOrderBy>(orderBy->clone());
    x->orderBy->setParent(x);
  }
}

SQLObject_ptr SQLUpdateStatement::clone()
{
  SQLObject_ptr clone_tmp = SQLUpdateStatement_ptr(new SQLUpdateStatement());
  SQLUpdateStatement_ptr x = std::dynamic_pointer_cast<SQLUpdateStatement>(clone_tmp);
  // SQLUpdateStatement *x = new SQLUpdateStatement();
  cloneTo(x);
  return x;
}

SQLUpdateStatement::SQLUpdateStatement(DbType_ptr dbType) : SQLStatementImpl(dbType)
{
}

SQLTableSource_ptr SQLUpdateStatement::getTableSource()
{
  return tableSource;
}

void SQLUpdateStatement::setTableSource(SQLExpr_ptr expr)
{
  this->setTableSource(SQLTableSource_ptr(new SQLExprTableSource(expr)));
}

void SQLUpdateStatement::setTableSource(SQLTableSource_ptr tableSource)
{
  if (tableSource != NULL)
  {
    tableSource->setParent(SharedObject(SQLUpdateStatement)); // static_cast<SQLObject_ptr >(static_cast<Object_ptr >(this)));
  }
  this->tableSource = tableSource;
}

SQLName_ptr SQLUpdateStatement::getTableName()
{
  if (instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
  {
    return (std::dynamic_pointer_cast<SQLExprTableSource>(tableSource))->getName();
  }

  if (instanceof <SQLTableSource, SQLJoinTableSource>(tableSource))
  {
    SQLTableSource_ptr left = (std::dynamic_pointer_cast<SQLJoinTableSource>(tableSource))->getLeft();
    if (instanceof <SQLTableSource, SQLExprTableSource>(left))
    {
      return (std::dynamic_pointer_cast<SQLExprTableSource>(left))->getName();
    }
  }
  return NULL;
}

SQLAssignItem_list_ptr SQLUpdateStatement::getPartitions()
{
  return partitions;
}

void SQLUpdateStatement::setPartitions(SQLAssignItem_list_ptr partitions)
{
  this->partitions->clear();
  for (auto part : *partitions)
  {
    this->partitions->push_back(part);
  }
}

SQLExpr_ptr SQLUpdateStatement::getWhere()
{
  return where;
}

void SQLUpdateStatement::setWhere(SQLExpr_ptr where)
{
  if (where != NULL)
  {
    where->setParent(SharedObject(SQLUpdateStatement)); // static_cast<SQLObject_ptr >(static_cast<Object_ptr >(this)));
  }
  this->where = where;
}

SQLUpdateSetItem_list_ptr SQLUpdateStatement::getItems()
{
  return items;
}

void SQLUpdateStatement::addItem(SQLUpdateSetItem_ptr item)
{
  this->items->push_back(item);
  item->setParent(SharedObject(SQLUpdateStatement));
}

SQLExpr_list_ptr SQLUpdateStatement::getReturning()
{
  // if (returning == NULL) {
  //     returning = new ArrayList<SQLExpr_ptr >(2);
  // }

  return returning;
}

SQLTableSource_ptr SQLUpdateStatement::getFrom()
{
  return from;
}

void SQLUpdateStatement::setFrom(SQLTableSource_ptr from)
{
  if (from != NULL)
  {
    from->setParent(SharedObject(SQLUpdateStatement)); // static_cast<SQLObject_ptr >(static_cast<Object_ptr >(this)));
  }
  this->from = from;
}

void SQLUpdateStatement::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLUpdateStatement)))
  {
    acceptChild(visitor);
  }
  visitor->endVisit(SharedObject(SQLUpdateStatement));
}

void SQLUpdateStatement::acceptChild(SQLASTVisitor_ptr visitor)
{
  if (with != NULL)
  {
    with->accept(visitor);
  }

  if (tableSource != NULL)
  {
    tableSource->accept(visitor);
  }

  if (from != NULL)
  {
    from->accept(visitor);
  }

  auto it_items = items->begin();
  for (int i = 0; i < items->size(); i++)
  {
    std::advance(it_items, i);
    SQLUpdateSetItem_ptr item = (*it_items);
    if (item != NULL)
    {
      item->accept(visitor);
    }
  }

  if (where != NULL)
  {
    where->accept(visitor);
  }

  if (orderBy != NULL)
  {
    orderBy->accept(visitor);
  }
}

SQLObject_list_ptr SQLUpdateStatement::getChildren()
{
  SQLObject_list_ptr children = std::make_shared<std::list<SQLObject_ptr>>();
  if (tableSource != NULL)
  {
    children->push_back(tableSource);
  }
  if (from != NULL)
  {
    children->push_back(from);
  }
  Utils::ListAddAll<SQLUpdateSetItem, SQLObject>(this->items, children);
  if (where != NULL)
  {
    children->push_back(where);
  }
  if (orderBy != NULL)
  {
    children->push_back(orderBy);
  }
  return children;
}

BOOL_ptr SQLUpdateStatement::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  if (where == expr)
  {
    setWhere(target);
    return BOOL::TRUE;
  }

  if (!returning->empty())
  {
    auto it_returning = returning->begin();
    for (int i = 0; i < returning->size(); i++)
    {
      std::advance(it_returning, i);
      if ((*it_returning) == expr)
      {
        target->setParent(SharedObject(SQLUpdateStatement)); // static_cast<SQLObject_ptr >(static_cast<Object_ptr >(this)));
        (*it_returning) = target;
        return BOOL::TRUE;
      }
    }
  }

  return BOOL::FALSE;
}

SQLOrderBy_ptr SQLUpdateStatement::getOrderBy()
{
  return orderBy;
}

void SQLUpdateStatement::setOrderBy(SQLOrderBy_ptr orderBy)
{
  if (orderBy != NULL)
  {
    orderBy->setParent(SharedObject(SQLUpdateStatement));
  }
  this->orderBy = orderBy;
}

SQLWithSubqueryClause_ptr SQLUpdateStatement::getWith()
{
  return with;
}

void SQLUpdateStatement::setWith(SQLWithSubqueryClause_ptr with)
{
  if (with != NULL)
  {
    with->setParent(SharedObject(SQLUpdateStatement));
  }
  this->with = with;
}

void SQLUpdateStatement::addCondition(string_ptr conditionSql)
{
  if (conditionSql->empty() || conditionSql->length() == 0)
  {
    return;
  }

  SQLExpr_ptr condition = SQLUtils::toSQLExpr(conditionSql, dbType);
  addCondition(condition);
}

void SQLUpdateStatement::addCondition(SQLExpr_ptr expr)
{
  if (expr == NULL)
  {
    return;
  }

  this->setWhere(SQLBinaryOpExpr::and_(where, expr));
}

BOOL_ptr SQLUpdateStatement::removeCondition(string_ptr conditionSql)
{
  if (conditionSql->empty() || conditionSql->length() == 0)
  {
    return BOOL::FALSE;
  }

  SQLExpr_ptr condition = SQLUtils::toSQLExpr(conditionSql, dbType);

  return removeCondition(condition);
}

BOOL_ptr SQLUpdateStatement::removeCondition(SQLExpr_ptr condition)
{
  if (condition == NULL)
  {
    return BOOL::FALSE;
  }

  if (instanceof <SQLExpr, SQLBinaryOpExprGroup>(where))
  {
    SQLBinaryOpExprGroup_ptr group = std::dynamic_pointer_cast<SQLBinaryOpExprGroup>(where);

    int removedCount = 0;
    SQLExpr_list_ptr items = group->getItems();
    auto it_items = items->begin();
    for (int i = items->size() - 1; i >= 0; i--)
    {
      std::advance(it_items, i);
      if ((*it_items)->equals(condition))
      {
        // items.remove(i);
        if (it_items != items->end()) // 最后一个不能删除
          items->erase(it_items);     // 删除第i个元素
        removedCount++;
      }
    }
    if (items->empty())
    {
      where = NULL;
    }

    return (removedCount > 0)?BOOL::TRUE:BOOL::FALSE;
  }

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(where))
  {
    SQLBinaryOpExpr_ptr binaryOpWhere = std::dynamic_pointer_cast<SQLBinaryOpExpr>(where);
    SQLBinaryOperator_ptr operator_ = binaryOpWhere->getOperator();
    if (operator_->name->c_str() == SQLBinaryOperator::BooleanAnd.name->c_str() ||
        operator_->name->c_str() == SQLBinaryOperator::BooleanOr.name->c_str())
    {
      SQLExpr_list_ptr items = SQLBinaryOpExpr::split(binaryOpWhere);

      int removedCount = 0;
      auto it_items = items->begin();
      for (int i = items->size() - 1; i >= 0; i--)
      {
        std::advance(it_items, i);
        SQLExpr_ptr item = (*it_items);
        if (item->equals(condition))
        {
          if (SQLUtils::replaceInParent(item, NULL))
          {
            removedCount++;
          }
        }
      }

      return (removedCount > 0)?BOOL::TRUE:BOOL::FALSE;
    }
  }

  if (condition->equals(where))
  {
    where = NULL;
    return BOOL::TRUE;
  }

  return BOOL::FALSE;
}

bool SQLUpdateStatement::equals(Object_ptr o)
{
  if (this->equals(o))
  {
    return true;
  }
  if (o == NULL || getClass() != o->getClass())
  {
    return false;
  }

  SQLUpdateStatement_ptr that = std::dynamic_pointer_cast<SQLUpdateStatement>(o);

  if (with != NULL ? !with->equals(that->with) : that->with != NULL)
  {
    return false;
  }
  if (!IFEQ::ifeq<SQLUpdateSetItem_ptr>(items, that->items))
  {
    return false;
  }
  if (where != NULL ? !where->equals(that->where) : that->where != NULL)
  {
    return false;
  }
  if (from != NULL ? !from->equals(that->from) : that->from != NULL)
  {
    return false;
  }
  if (tableSource != NULL ? !tableSource->equals(that->tableSource) : that->tableSource != NULL)
  {
    return false;
  }
  if (!returning->empty() ? !IFEQ::ifeq<SQLExpr_ptr>(returning, that->returning) : !that->returning->empty())
  {
    return false;
  }
  return orderBy != NULL ? orderBy->equals(that->orderBy) : that->orderBy == NULL;
}

int SQLUpdateStatement::hashCode()
{
  int result = with != NULL ? with->hashCode() : 0;
  // result = 31 * result + items->hashCode();
  result = 31 * result + (where != NULL ? where->hashCode() : 0);
  result = 31 * result + (from != NULL ? from->hashCode() : 0);
  result = 31 * result + (tableSource != NULL ? tableSource->hashCode() : 0);
  // result = 31 * result + (returning != NULL ? returning->hashCode() : 0);
  result = 31 * result + (orderBy != NULL ? orderBy->hashCode() : 0);
  return result;
}

BOOL_ptr SQLUpdateStatement::addWhere(SQLExpr_ptr where)
{
  if (where == NULL)
  {
    return BOOL::FALSE;
  }

  this->addCondition(where);
  return BOOL::TRUE;
}
